/*=============================================================================
  D3DTexturesStreaming.cpp : Direct3D9 specific texture streaming technology.
  Copyright (c) 2001 Crytek Studios. All Rights Reserved.

  Revision history:
										* Created by Honitch Andrey
- 19:8:2008   12:14 : Refactored by Kaplanyan Anton

=============================================================================*/

#include "StdAfx.h"
#include "DriverD3D.h"
#include "StringUtils.h"
#include <IJobManager_JobDelegator.h>

//===============================================================================

#if defined (DIRECT3D10) && !defined (PS3) 	
#define STREAMED_TEXTURE_USAGE (CDeviceManager::USAGE_STREAMING)
#else
#define STREAMED_TEXTURE_USAGE 0
#endif

#if (defined(PS3) || defined(CAFE)) && !defined(__SPU__) && !defined(__CRYCG__)
void Texture_CopyToVMem( uint8 *pDst, const byte * pSrc, size_t nSize ){ memcpy(pDst, pSrc, nSize); }
DECLARE_JOB("Texture_CopyToVMem", TCopyToVMemJob, Texture_CopyToVMem );	
DECLARE_JOB("Texture_ExpandMipFromFile", TTexture_ExpandMipFromFileJob, CTexture::ExpandMipFromFile );
#endif

// checks for MT-safety of called functions
#define CHK_RENDTH assert(gcpRendD3D->m_pRT->IsRenderThread())
#define CHK_MAINTH assert(gcpRendD3D->m_pRT->IsMainThread())
#define CHK_MAINORRENDTH assert(gRenDev->m_pRT->IsMainThread() || gRenDev->m_pRT->IsRenderThread())

bool STexPoolItem::IsStillUsedByGPU()
{
	CDeviceTexture* pDeviceTexture = m_pDevTexture;
	if(pDeviceTexture)
	{
    CHK_MAINORRENDTH;
		D3DBaseTexture *pD3DTex = pDeviceTexture->GetBaseTexture();





	}
	return false;
}

STexPoolItem::~STexPoolItem()
{
	Unlink();
	UnlinkFree();

  if(m_pDevTexture || m_nDeviceTexSize)
	{
		const int nSize = m_pOwner ? m_pOwner->m_Size : m_nDeviceTexSize;
		assert(CTexture::s_nStatsCurManagedStreamedTexMem >= nSize);
		CryInterlockedAdd(CTexture::s_nStatsCurManagedStreamedTexMem.Addr(), -nSize);
	}

	assert(!IsStillUsedByGPU());
#if !defined (XENON)













  SAFE_RELEASE(m_pDevTexture); 









#endif	
}

bool CTexture::IsStillUsedByGPU()
{
	CDeviceTexture* pDeviceTexture = m_pDevTexture;
	if (pDeviceTexture)
	{
		CHK_RENDTH;
		D3DBaseTexture *pD3DTex = pDeviceTexture->GetBaseTexture();









	}
	return false;
}














































































































































#if defined (DIRECT3D9) || defined(XENON) || defined(PS3) || defined(CAFE)
// streaming thread
void CTextureUnloadCallback::StreamAsyncOnComplete(IReadStream *pStream, unsigned nError)
{
  STexAsyncUnloadInfo *pTexLodInfo = (STexAsyncUnloadInfo *)pStream->GetUserData();
  assert(pTexLodInfo->m_pTexture);

  CTexture *tp = pTexLodInfo->m_pTexture;
  if (pStream->IsError() || !tp || !tp->IsStreamed())
	{
		pStream->FreeTemporaryMemory();
		return;
	}





#if !defined(PS3) && !defined(CAFE)
	// it is a sync operation anyway, so we do it in the render thread
	const int nOldMipOffset = pTexLodInfo->m_nStartLoadMip - tp->m_nMinMipVidUploaded;
	const int nNumMips = tp->GetNumMipsNonVirtual() - pTexLodInfo->m_nStartLoadMip;
	CTexture::StreamCopyMipsTexToTex(tp->m_pFileTexMips->m_pPoolItem, 0 + nOldMipOffset, pTexLodInfo->m_pNewPoolItem, 0, nNumMips);
#endif

	pStream->FreeTemporaryMemory();
}

// main thread
void CTextureUnloadCallback::StreamOnComplete (IReadStream* pStream, unsigned nError)
{
	CHK_MAINORRENDTH;

	STexAsyncUnloadInfo* pTexLodInfo = (STexAsyncUnloadInfo*)pStream->GetUserData();

	if(!pTexLodInfo)
	{
		CTexture::StreamRemoveTask(NULL, IReadStreamPtr(pStream));
		assert(0);
		return;
	}

	CTexture* tp = pTexLodInfo->m_pTexture;

	CTexture::StreamRemoveTask(tp, IReadStreamPtr(pStream));

	// check for any possible error
	if(nError || !tp || !tp->m_bStreamPrepared)
	{
		SAFE_DELETE(pTexLodInfo);
		pStream->SetUserData(NULL);
		if(tp)
		{
			tp->SetStreamingInProgress(false);
			tp->Release();
		}
		return;
	}

	// add to the queue
	pStream->AddRef();
  gRenDev->m_pRT->RC_TexStreamComplete(pStream);
}

// consoles-only, render thread



































int CTexture::StreamSetLod(int nToMip)
{
	CHK_RENDTH;

	if (IsUnloaded() || !IsStreamed() || m_bStreamingInProgress)
    return 0;

	// clamp mip level 
	nToMip = max(0, min(nToMip, m_nMips-m_CacheFileHeader.m_nMipsPersistent));

  if (m_nMinMipVidUploaded >= nToMip)
    return 0;

	m_pFileTexMips->m_nStreamingSwitchUpdateId = m_pFileTexMips->m_arrSPInfo[MAX_PREDICTION_ZONES-1].nRoundUpdateId;
  // Make sure we don't have to request any async loading at this stage
  assert(!m_bStreamingInProgress);
	int nFreeSize = m_pFileTexMips->m_pMipHeader[m_nMinMipVidUploaded].m_SideSizeWithMips - m_pFileTexMips->m_pMipHeader[nToMip].m_SideSizeWithMips;
	if(m_eTT == eTT_Cube) nFreeSize *= 6;

#ifndef _RELEASE
	if (CRenderer::CV_r_TexturesStreamingDebug == 2)
		iLog->Log("Shrinking texture: %s - From mip: %i, To mip: %i", m_SrcName.c_str(), m_nMinMipVidUploaded, m_pFileTexMips->m_nMinMipCur);
#endif





	m_nMinMipVidUploaded = nToMip;

	// Release video-memory
	D3DBaseTexture *pBaseTexture = m_pDevTexture->GetBaseTexture();
	assert(pBaseTexture->GetLevelCount() > nToMip);
	pBaseTexture->SetLOD(nToMip);

	assert(m_nActualSize >= nFreeSize);
	CryInterlockedAdd(&m_nActualSize, -nFreeSize);
	assert(CTexture::s_nStatsCurManagedStreamedTexMem >= nFreeSize);
	CryInterlockedAdd(CTexture::s_nStatsCurManagedStreamedTexMem.Addr(), -nFreeSize);

	return nFreeSize;

}

#elif defined (DIRECT3D10)
void CTexture::_StreamSetLod(int nToMip)
{
  m_nMinMipVidUploaded = nToMip;

  CD3D9Renderer *r = gcpRendD3D;
  D3DDevice *dv = r->GetD3DDevice();
  HRESULT hr = S_OK;
  D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc;
  ZeroStruct(SRVDesc);
  SRVDesc.Format = DeviceFormatFromTexFormat(GetDstFormat());
  if (m_bIsSRGB && gRenDev->IsLinearSpaceShadingEnabled() != 0)
    SRVDesc.Format = ConvertToSRGBFmt(SRVDesc.Format);
  ID3D11ShaderResourceView *pSRV = (ID3D11ShaderResourceView *)m_pDeviceShaderResource;
  SAFE_RELEASE(pSRV);

  // Release video-memory
  if (m_eTT != eTT_Cube)
  {
    SRVDesc.Texture2D.MipLevels = m_nMips - nToMip;
    SRVDesc.Texture2D.MostDetailedMip = nToMip;
    SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    hr = dv->CreateShaderResourceView(m_pDevTexture->Get2DTexture(), &SRVDesc, &pSRV);
  }
  else
  {
    SRVDesc.TextureCube.MipLevels = m_nMips - nToMip;
    SRVDesc.TextureCube.MostDetailedMip = nToMip;
    SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
    hr = dv->CreateShaderResourceView(m_pDevTexture->GetCubeTexture(), &SRVDesc, &pSRV);
  }
  m_pDeviceShaderResource = pSRV;
  assert(hr == S_OK);
}
int CTexture::StreamSetLod(int nToMip)
{
  if (IsUnloaded() || !IsStreamed())
    return 0;

	// clamp mip level 
	nToMip = min(nToMip, m_nMips-m_CacheFileHeader.m_nMipsPersistent);

  if (m_nMinMipVidUploaded >= nToMip)
    return 0;

	// Make sure we don't have to request any async loading at this stage
	if (m_bStreamingInProgress)
	{
		AbortStreamingTasks(this);
		assert(!m_bStreamingInProgress);
	}

  const STexStreamingInfo::SMipHeader *mh = m_pFileTexMips->m_pMipHeader;

  int nFreeSize = m_pFileTexMips->m_pMipHeader[m_nMinMipVidUploaded].m_SideSizeWithMips - m_pFileTexMips->m_pMipHeader[nToMip].m_SideSizeWithMips;
	nFreeSize *= m_CacheFileHeader.m_nSides;
  if (m_eTT == eTT_Cube)
    nFreeSize *= 6;

  _StreamSetLod(nToMip);


	assert(m_nActualSize >= nFreeSize);
	CryInterlockedAdd(&m_nActualSize, -nFreeSize);
	assert(CTexture::s_nStatsCurManagedStreamedTexMem >= nFreeSize);
	CryInterlockedAdd(CTexture::s_nStatsCurManagedStreamedTexMem.Addr(), -nFreeSize);

#ifndef _RELEASE
	if (CRenderer::CV_r_TexturesStreamingDebug == 2)
		iLog->Log("Shrinking texture: %s - From mip: %i, To mip: %i", m_SrcName.c_str(), m_nMinMipVidUploaded, m_pFileTexMips->m_nMinMipCur);
#endif

  return nFreeSize;
}
#endif

#if defined (DIRECT3D9) || defined(XENON)
void CTexture::StreamCopyMipsTexToMem(int nStartMip, int nEndMip, bool bToDevice, STexPoolItem* pNewPoolItem)
{
	PROFILE_FRAME(Texture_StreamUpload);
	SCOPED_RENDERER_ALLOCATION_NAME_HINT( GetSourceName() );

	CDeviceManager *pDevMan = &gcpRendD3D->m_DevMan;
	HRESULT h;
	STexStreamingInfo::SMipHeader *mh = m_pFileTexMips->m_pMipHeader;
	bool bIsTextureCreated = false;

	const int nOldMinMipVidUploaded = m_nMinMipVidUploaded;

	if(bToDevice && !pNewPoolItem)
		m_nMinMipVidUploaded = nStartMip;





	D3DFormat d3dFmt = DeviceFormatFromTexFormat(GetDstFormat());
	if ( m_bIsSRGB && gRenDev->IsLinearSpaceShadingEnabled() )
		d3dFmt = CTexture::ConvertToSRGBFmt(d3dFmt);

	if (m_eTT != eTT_Cube)
	{
		CDeviceTexture *pDevTexture = NULL;
		if (bToDevice && pNewPoolItem)
		{
			pDevTexture = pNewPoolItem->m_pDevTexture;
			assert(pDevTexture);
		}
		else
			pDevTexture = m_pDevTexture;
		if (!pDevTexture && bToDevice)
		{




			PROFILE_FRAME(Texture_StreamCreate);
			if (FAILED(h = pDevMan->Create2DTexture(m_nWidth, m_nHeight, m_nMips, STREAMED_TEXTURE_USAGE, d3dFmt, TEXPOOL, &pDevTexture)))
			{
				assert(false);
				return;
			}
      assert(!m_pDevTexture);
      CryInterlockedAdd(CTexture::s_nStatsCurManagedStreamedSysTexMem.Addr(), m_nFullSize);
			bIsTextureCreated = true;
      SAFE_RELEASE(m_pDevTexture); 
			m_pDevTexture = pDevTexture;

		}

		D3DTexture *pID3DTexture = pDevTexture->Get2DTexture();




		static const int nMipOffset = 0;


		if (CRenderer::CV_r_texturesstreamingnoupload && bToDevice)
			return;

		int SizeToLoad = 0;
		if (m_pFileTexMips->m_pPoolItem)
		{
			assert((int)pID3DTexture->GetLevelCount() >= (nEndMip - nStartMip + 1));
			for (int i=nStartMip; i<=nEndMip; i++)
			{
				int nLod = i - nMipOffset;
				SMipData *mp = &mh[i].m_Mips[0];

				if (bToDevice)
					CryInterlockedAdd(&CTexture::s_nTexturesDataBytesUploaded, mh[i].m_SideSize);
				STexLock lockedRect;
#if !defined(XENON)
				D3DLOCKED_RECT d3dlr;
				D3DTexture* pSysTexture = m_pFileTexMips->m_pPoolItem->m_pOwner->m_pSysTexture->Get2DTexture();
				h = pSysTexture->LockRect(nLod, &d3dlr, NULL, 0);
				lockedRect.pData = (uint8*)d3dlr.pBits;
				lockedRect.Pitch = d3dlr.Pitch;


#endif
				assert(h == S_OK);
				SizeToLoad += mh[i].m_SideSize;
				if(FAILED(h) || !lockedRect.pData)
					continue;
#ifndef XENON
				bool bLoaded = false;
				if (mh[i].m_USize < 4 && !IsDXTCompressed(m_eTFDst))
				{
					int nD3DSize = d3dlr.Pitch * mh[i].m_VSize;
					if (nD3DSize != mh[nLod].m_SideSize)
					{
						bLoaded = true;
						byte *pDst = (byte *)d3dlr.pBits;
						byte *pSrc = &mp->DataArray[0];
						int nPitchSrc = CTexture::TextureDataSize(mh[i].m_USize, 1, 1, 1, m_eTFDst);
						if (bToDevice)
						{
							for (int j=0; j<mh[i].m_VSize; j++)
							{
								memcpy(pDst, pSrc, nPitchSrc);
								pSrc += nPitchSrc;
								pDst += d3dlr.Pitch;
							}
						}
						else
						{
							for (int j=0; j<mh[i].m_VSize; j++)
							{
								memcpy(pSrc, pDst, nPitchSrc);
								pSrc += nPitchSrc;
								pDst += d3dlr.Pitch;
							}
						}
					}
				}
				if (!bLoaded)
				{
					// Copy data to system texture 
					if (bToDevice)
					{
						assert(mp->DataArray);
						memcpy((byte *)d3dlr.pBits, &mp->DataArray[0], mh[i].m_SideSize);
					}
					else
					{
						if(!mp->DataArray)
							mp->Init(mh[i].m_SideSize, mh[i].m_USize, mh[i].m_VSize);
						memcpy(&mp->DataArray[0], (byte *)d3dlr.pBits, mh[i].m_SideSize);
					}
				}























































#endif

#if !defined(XENON)
				// Unlock the system texture
				pSysTexture->UnlockRect(nLod);
				D3DSurface* pSrcSurf = NULL;
				D3DSurface* pDestSurf = NULL;
				pID3DTexture->GetSurfaceLevel(nLod, &pDestSurf);
				pSysTexture->GetSurfaceLevel(nLod, &pSrcSurf);
				if (bToDevice)
					h = gcpRendD3D->m_pd3dDevice->UpdateSurface(pSrcSurf, 0, pDestSurf, 0);
				else
					D3DXLoadSurfaceFromSurface(pSrcSurf, NULL, NULL, pDestSurf, NULL, NULL, D3DX_FILTER_NONE, 0);
				SAFE_RELEASE(pDestSurf);
				SAFE_RELEASE(pSrcSurf);


#endif

				if(s_bStreamDontKeepSystem && bToDevice)
					mp->Free();
			}
		}
		else
		{
			for (int i=nStartMip; i<=nEndMip; i++)
			{
				int nLod = i - nMipOffset;
				assert(nLod >= 0);
				SMipData *mp = &mh[i].m_Mips[0];

				if(bToDevice && !mp->DataArray && s_bStreamDontKeepSystem)	// we have this mip already loaded
				{
					continue;
				}

				if (bToDevice)
				{
#if !defined(XENON)
					CryInterlockedAdd(&m_nActualSize, mh[i].m_SideSize);
					CryInterlockedAdd(CTexture::s_nStatsCurManagedStreamedTexMem.Addr(), mh[i].m_SideSize);
					CTexture::StreamValidateTexSize();
#endif
					CryInterlockedAdd(&CTexture::s_nTexturesDataBytesUploaded, mh[i].m_SideSize);
				}

				if (!mp->DataArray)
					mp->Init(mh[i].m_SideSize, mh[i].m_USize, mh[i].m_VSize);

				STexLock lockedRect;

#if !defined(XENON)
				h = pDevTexture->LockRect(nLod, lockedRect, bToDevice?0:D3DLOCK_READONLY);


#endif
				assert(h == S_OK);

				if (SUCCEEDED(h) && lockedRect.pData)
				{
					bool bLoaded = false;
					SizeToLoad += m_pFileTexMips->m_pMipHeader[i].m_SideSize;
					if (mh[i].m_USize < 4 && !IsDXTCompressed(m_eTFDst))
					{
						int nD3DSize = lockedRect.Pitch * mh[i].m_VSize;
						if (nD3DSize != m_pFileTexMips->m_pMipHeader[i].m_SideSize)
						{
							bLoaded = true;
							byte *pDst = (byte *)lockedRect.pData;
							byte *pSrc = &mp->DataArray[0];

							int nPitchSrc = CTexture::TextureDataSize(mh[i].m_USize, 1, 1, 1, m_eTFDst);
							if (bToDevice)
							{
								for (int j=0; j<mh[i].m_VSize; j++)
								{
									memcpy(pDst, pSrc, nPitchSrc);
									pSrc += nPitchSrc;
									pDst += lockedRect.Pitch;
								}
							}
							else
							{
								for (int j=0; j<mh[i].m_VSize; j++)
								{
									memcpy(pSrc, pDst, nPitchSrc);
									pSrc += nPitchSrc;
									pDst += lockedRect.Pitch;
								}
							}
						}
					}
					if (!bLoaded)
					{
						if(mp->DataArray)
						{
							// Copy data to/from video texture 
							int nPitch = CTexture::TextureDataSize(mh[i].m_USize, 1, 1, 1, m_eTFDst);
							assert(mh[i].m_SideSize % nPitch == 0);
							if (bToDevice)
								memcpy((byte *)lockedRect.pData, &mp->DataArray[0], mh[i].m_SideSize);
							else
								memcpy(&mp->DataArray[0], (byte *)lockedRect.pData, mh[i].m_SideSize);
						}
						else
							assert(0);
					}
					// Unlock the video texture
					h = pID3DTexture->UnlockRect(nLod);
					assert(h == S_OK);
				}
				
				if(s_bStreamDontKeepSystem && bToDevice)
					mp->Free();
			}
#if !defined(XENON)
			assert(m_nMinMipVidUploaded < pID3DTexture->GetLevelCount());
			int nOldMip = pID3DTexture->SetLOD(m_nMinMipVidUploaded);
			if(!bIsTextureCreated)
				assert(nOldMinMipVidUploaded == nOldMip || (nOldMinMipVidUploaded == MAX_MIP_LEVELS && nOldMip == 0));
#endif
		}
#ifdef DO_RENDERLOG
		if (gRenDev->m_LogFileStr)
			gRenDev->LogStrv(SRendItem::m_RecurseLevel[gRenDev->m_RP.m_nProcessThreadID], "Uploading mips '%s'. (Side: %d, %d-%d[%d]), Size: %d, Time: %.3f\n", m_SrcName.c_str(), -1, nStartMip, nEndMip, m_nMips, SizeToLoad, iTimer->GetAsyncCurTime());
#endif
	}
	else
	{
		CDeviceTexture *pDevTexture = NULL;
		if (bToDevice && pNewPoolItem)
		{
			pDevTexture = pNewPoolItem->m_pDevTexture;
			assert(pDevTexture);
		}
		else
			pDevTexture = m_pDevTexture;
		if (!pDevTexture && bToDevice)
		{




			if( FAILED(h = pDevMan->CreateCubeTexture(m_nWidth, m_nMips, STREAMED_TEXTURE_USAGE, d3dFmt, TEXPOOL, &pDevTexture, NULL)))
			{
				assert(false);
				return;
			}
      assert(!m_pDevTexture);
      CryInterlockedAdd(CTexture::s_nStatsCurManagedStreamedSysTexMem.Addr(), m_nFullSize);
      SAFE_RELEASE(m_pDevTexture); 
			m_pDevTexture = pDevTexture;

		}
    D3DCubeTexture *pID3DCubeTexture = pDevTexture->GetCubeTexture();

		const int nMipOffset = m_nMips - pID3DCubeTexture->GetLevelCount();

		if (!m_pFileTexMips->m_pPoolItem)
		{
#if !defined(XENON)
			m_nMinMipVidUploaded = nStartMip;
			pID3DCubeTexture->SetLOD(nStartMip);
#endif
		}

#	if defined (_DEBUG)
		D3DSURFACE_DESC mainLevelDesc;
		pID3DCubeTexture->GetLevelDesc(0, &mainLevelDesc);
#	endif

		for (int iSide=0; iSide<6; iSide++)
		{
			int SizeToLoad = 0;
			if (m_pFileTexMips->m_pPoolItem)
			{
				for (int iMip=nStartMip; iMip<=nEndMip; iMip++)
				{
					const int nLod = iMip - nMipOffset;
					SMipData *mp = &mh[iMip].m_Mips[iSide];

					if (bToDevice)
						CryInterlockedAdd(&CTexture::s_nTexturesDataBytesUploaded, mh[iMip].m_SideSize);
					D3DLOCKED_RECT d3dlr;
#if !defined(XENON)
					D3DSurface* pDestSurf;
					D3DSurface* pSrcSurf;
					h = pID3DCubeTexture->GetCubeMapSurface((D3DCUBEMAP_FACES)iSide, nLod, &pDestSurf);
					h = m_pFileTexMips->m_pPoolItem->m_pOwner->m_pSysTexture->GetCubeTexture()->GetCubeMapSurface((D3DCUBEMAP_FACES)iSide, nLod, &pSrcSurf);
					h = pSrcSurf->LockRect(&d3dlr, NULL, 0);


#endif
#if defined (_DEBUG) && !defined(XENON)
					int nD3DSize;
					if (IsDXTCompressed(GetDstFormat()))
						nD3DSize = d3dlr.Pitch * (mh[iMip].m_VSize + 3) / 4;
					else
						nD3DSize = d3dlr.Pitch * mh[iMip].m_VSize;
					assert(nD3DSize == mh[iMip].m_SideSize);
#endif
					SizeToLoad += mh[iMip].m_SideSize;
#ifndef XENON
					// Copy data to system texture 
					if (bToDevice)
						memcpy((byte *)d3dlr.pBits, &mp->DataArray[0], mh[iMip].m_SideSize);
					else
						memcpy(&mp->DataArray[0], (byte *)d3dlr.pBits, mh[iMip].m_SideSize);

















































#endif
					// Unlock the system texture
#if !defined(XENON)
					pSrcSurf->UnlockRect();
					h = gcpRendD3D->m_pd3dDevice->UpdateSurface(pSrcSurf, 0, pDestSurf, 0);
					SAFE_RELEASE(pDestSurf);
					SAFE_RELEASE(pSrcSurf);


#endif
				}
			}
			else
			{
				for (int i=nStartMip; i<=nEndMip; i++)
				{
					SMipData *mp = &mh[i].m_Mips[iSide];
					if (!mp->DataArray)
						continue;
					if (bToDevice)
					{
#if !defined(XENON)
						CryInterlockedAdd(&m_nActualSize, mh[i].m_SideSize);
						CryInterlockedAdd(CTexture::s_nStatsCurManagedStreamedTexMem.Addr(), mh[i].m_SideSize);
						CTexture::StreamValidateTexSize();
#endif
						CryInterlockedAdd(&CTexture::s_nTexturesDataBytesUploaded, mh[i].m_SideSize);
					}
					D3DLOCKED_RECT d3dlr;
					h = pID3DCubeTexture->LockRect((D3DCUBEMAP_FACES)iSide, i, &d3dlr, NULL, 0);
#ifdef _DEBUG
					if (mh[i].m_USize >= 4)
					{
						int nD3DSize;
						if (IsDXTCompressed(GetDstFormat()))
							nD3DSize = d3dlr.Pitch * (mh[i].m_VSize + 3) / 4;
						else
							nD3DSize = d3dlr.Pitch * mh[i].m_VSize;
						assert(nD3DSize == mh[i].m_SideSize);
					}
#endif

					bool bLoaded = false;
					SizeToLoad += mh[i].m_SideSize;
					if (mh[i].m_USize < 4 && !IsDXTCompressed(m_eTFDst))
					{
						int nD3DSize = d3dlr.Pitch * mh[i].m_VSize;
						if (nD3DSize != m_pFileTexMips->m_pMipHeader[i].m_SideSize)
						{
							bLoaded = true;
							byte *pDst = (byte *)d3dlr.pBits;
							byte *pSrc = &mp->DataArray[0];
							int nPitchSrc = CTexture::TextureDataSize(mh[i].m_USize, 1, 1, 1, m_eTFDst);
							if (bToDevice)
							{
								for (int j=0; j<mh[i].m_VSize; j++)
								{
									memcpy(pDst, pSrc, nPitchSrc);
									pSrc += nPitchSrc;
									pDst += d3dlr.Pitch;
								}
							}
							else
							{
								for (int j=0; j<mh[i].m_VSize; j++)
								{
									memcpy(pSrc, pDst, nPitchSrc);
									pSrc += nPitchSrc;
									pDst += d3dlr.Pitch;
								}
							}
						}
					}

					if (!bLoaded)
					{
						// Copy data to video texture 
						if (bToDevice)
							memcpy((byte *)d3dlr.pBits, &mp->DataArray[0], mh[i].m_SideSize);
						else
							memcpy(&mp->DataArray[0], (byte *)d3dlr.pBits, mh[i].m_SideSize);
					}

					// Unlock the video texture
					pID3DCubeTexture->UnlockRect((D3DCUBEMAP_FACES)iSide, i);
				}
			}
#ifdef DO_RENDERLOG
			if (gRenDev->m_LogFileStr)
				gRenDev->LogStrv(SRendItem::m_RecurseLevel[gRenDev->m_RP.m_nProcessThreadID], "Uploading mips '%s'. (Side: %d, %d-%d[%d]), Size: %d, Time: %.3f\n", m_SrcName.c_str(), iSide, nStartMip, nEndMip, m_nMips, SizeToLoad, iTimer->GetAsyncCurTime());
#endif
		}
	}
}

#elif defined(DIRECT3D10) || defined(PS3) || defined(CAFE)
void CTexture::StreamCopyMipsTexToMem(int nStartMip, int nEndMip, bool bToDevice, STexPoolItem* pNewPoolItem)
{
	PROFILE_FRAME(Texture_StreamUpload);

	CD3D9Renderer *r = gcpRendD3D;
	CDeviceManager *pDevMan = &r->m_DevMan;
	HRESULT h = S_OK;
	nEndMip	=	min(nEndMip+1,m_pFileTexMips->m_nNumMips)-1;//+1 -1 needed as the compare is <= 
	STexStreamingInfo::SMipHeader *mh = m_pFileTexMips->m_pMipHeader;

  const int nOldMinMipVidUploaded = m_nMinMipVidUploaded;

  if (bToDevice && !pNewPoolItem)
    m_nMinMipVidUploaded = nStartMip;

	D3DFormat fmt = DeviceFormatFromTexFormat(GetDstFormat());
	if (m_bIsSRGB && gRenDev->IsLinearSpaceShadingEnabled())
		fmt = ConvertToSRGBFmt(fmt);

	LPDEVICETEXTURE* ppDevTexture = &m_pDevTexture;
	uint32 nTexMips = m_nMips;
	if(m_pFileTexMips->m_pPoolItem)
	{
		assert(m_pFileTexMips->m_pPoolItem->m_pDevTexture);
		assert(*ppDevTexture == m_pFileTexMips->m_pPoolItem->m_pDevTexture);
		nTexMips = m_pFileTexMips->m_pPoolItem->m_pOwner->m_nMips;
	}
	if (bToDevice && pNewPoolItem)
	{
		assert(pNewPoolItem->m_pDevTexture);
		ppDevTexture = &pNewPoolItem->m_pDevTexture;
		nTexMips = pNewPoolItem->m_pOwner->m_nMips;
	}

#if !defined(PS3) && !defined(CAFE)
	if (!(*ppDevTexture))
	{
		if (m_eTT != eTT_Cube)
			h = pDevMan->Create2DTexture(m_nWidth, m_nHeight, m_nMips, STREAMED_TEXTURE_USAGE, fmt, (D3DPOOL)0, ppDevTexture);
		else
			h = pDevMan->CreateCubeTexture(m_nWidth, m_nMips, STREAMED_TEXTURE_USAGE, fmt, (D3DPOOL)0, ppDevTexture);
		assert(h == S_OK);
	}


#endif

	if (CRenderer::CV_r_texturesstreamingnoupload && bToDevice)
		return;






	const int nMipOffset = m_nMips - nTexMips;
	const int32 nSides = m_eTT == eTT_Cube ? 6 : 1;

	D3DBaseTexture *pID3DTexture = (*ppDevTexture)->GetBaseTexture();

	int SizeToLoad = 0;
	for (int32 iSide=0; iSide<nSides; ++iSide)
	{
		const int32 iSideLockIndex = m_eTT != eTT_Cube ? -1 : iSide;
		for (int nLod=nStartMip; nLod<=nEndMip; nLod++)
		{
			SMipData *mp = &mh[nLod].m_Mips[iSide];

			if(bToDevice && !mp->DataArray && s_bStreamDontKeepSystem)	// we have this mip already loaded
				continue;

			const int nDevTexMip = nLod - nMipOffset;

			if (bToDevice)
			{
				if(mp->DataArray)
				{
	#if !defined(PS3)
					CryInterlockedAdd(&m_nActualSize, mh[nLod].m_SideSize);
					CryInterlockedAdd(CTexture::s_nStatsCurManagedStreamedTexMem.Addr(), mh[nLod].m_SideSize);
					CTexture::StreamValidateTexSize();
	#endif
					CryInterlockedAdd(&CTexture::s_nTexturesDataBytesUploaded, mh[nLod].m_SideSize);
					const int nRowPitch = CTexture::TextureDataSize(mh[nLod].m_USize, 1, 1, 1, m_eTFDst);
					STALL_PROFILER("update texture");















					{
						gcpRendD3D->m_pd3dDeviceContext->UpdateSubresource(pID3DTexture, D3D11CalcSubresource(nDevTexMip, iSide, nTexMips), NULL, &mp->DataArray[0], nRowPitch, 0);
					}
				}
				else
					assert(0);
			}
			else
			{
				const int nMipSize = mh[nLod].m_SideSize;
				mp->Init(nMipSize, mh[nLod].m_USize, mh[nLod].m_VSize);
				const int nRowPitch = CTexture::TextureDataSize(mh[nLod].m_USize, 1, 1, 1, m_eTFDst);
				const int nRows = nMipSize / nRowPitch;
				assert(nMipSize%nRowPitch == 0);
				STexLock rect;
				STALL_PROFILER("update texture");
				HRESULT hr = (*ppDevTexture)->LockRect(iSideLockIndex, nDevTexMip, rect, LF_READ | LF_STREAMING);
				assert(SUCCEEDED(hr));
        if (hr != S_OK)
        {
          int nnn = 0;
        }
				for(int iRow = 0;iRow < nRows;++iRow)
					memcpy(&mp->DataArray[iRow * nRowPitch], (byte*)rect.pData + rect.Pitch * iRow, nRowPitch);
				hr = (*ppDevTexture)->UnlockRect(iSideLockIndex, nDevTexMip);
				assert(SUCCEEDED(hr));
				// mark as native
				mp->m_bNative = true;
			}
			SizeToLoad += m_pFileTexMips->m_pMipHeader[nLod].m_SideSize;

			if(s_bStreamDontKeepSystem && bToDevice)
				mp->Free();
		}
	}
#ifdef DO_RENDERLOG
	if (gRenDev->m_LogFileStr)
		gRenDev->LogStrv(SRendItem::m_RecurseLevel[gRenDev->m_RP.m_nProcessThreadID], "Uploading mips '%s'. (%d[%d]), Size: %d, Time: %.3f\n", m_SrcName.c_str(), nStartMip, m_nMips, SizeToLoad, iTimer->GetAsyncCurTime());
#endif

#if !defined(PS3) && !defined(CAFE)
  _StreamSetLod(m_nMinMipVidUploaded);
#endif
}

#endif

// Just remove item from the texture object and keep Item in Pool list for future use
// This function doesn't release API texture
void CTexture::StreamRemoveFromPool()
{
	CHK_MAINORRENDTH;

	if (!m_pFileTexMips || !m_pFileTexMips->m_pPoolItem)
		return;

	AUTO_LOCK(STexPoolItem::s_sSyncLock);

#if defined(DIRECT3D10)	
	SAFE_RELEASE(*alias_cast<ID3D11ShaderResourceView**>(&m_pDeviceShaderResource));
#endif

	STexPoolItem *pIT = m_pFileTexMips->m_pPoolItem;
	assert(!pIT->m_NextFree);
	m_pFileTexMips->m_pPoolItem = NULL;
	pIT->m_pTex = NULL;

	m_nActualSize = 0;
	m_pDevTexture = NULL;

	pIT->Unlink();
	pIT->LinkFree(&CTexture::s_FreeTexPoolItems);
	m_nMinMipVidUploaded = MAX_MIP_LEVELS;
#ifdef DO_RENDERLOG
	if (CRenderer::CV_r_logTexStreaming == 2)
		gRenDev->LogStrv(SRendItem::m_RecurseLevel[gRenDev->m_RP.m_nProcessThreadID], "Add to FreePool '%s', [%d x %d], Size: %d\n", m_SrcName.c_str(), pIT->m_pOwner->m_Width, pIT->m_pOwner->m_Height, pIT->m_pOwner->m_Size);
#endif
}

void CTexture::StreamAssignPoolItem(STexPoolItem *pItem, int nMinMip)
{
	if(m_pFileTexMips->m_pPoolItem == pItem)
	{
		assert(m_nActualSize == m_pFileTexMips->m_pPoolItem->m_pOwner->m_Size);
		assert(m_pFileTexMips->m_pPoolItem->m_pTex == this);
		assert(m_pDevTexture == m_pFileTexMips->m_pPoolItem->m_pDevTexture);
		return;
	}

	// if we have it assigned at the first time after StreamPrepare(), we need to release old texture
	if(m_pFileTexMips->m_pPoolItem == NULL)
	{
		if(m_pDevTexture)
		{
#if !defined(XENON) && !defined(PS3) && !defined(CAFE)
			SAFE_RELEASE(m_pDevTexture);


#endif
		}
	}

	// Assign a new pool item
	{
		AUTO_LOCK(STexPoolItem::s_sSyncLock);
		StreamRemoveFromPool();

		m_pFileTexMips->m_pPoolItem = pItem;
		m_pFileTexMips->m_pPoolItem->UnlinkFree();
		m_nActualSize = m_pFileTexMips->m_pPoolItem->m_pOwner->m_Size;
		m_pFileTexMips->m_pPoolItem->m_pTex = this;
	}

#if !(defined(XENON) && defined(ENABLE_X360_TEXTURE_CAPTURE))
  SAFE_RELEASE(m_pDevTexture);
  m_pDevTexture = m_pFileTexMips->m_pPoolItem->m_pDevTexture;
#else	// we need to replace the base address without touching the texture
	if(m_pDevTexture == NULL)
	{
		D3DBaseTexture* pRawTexture = NULL;
		if(m_eTT != eTT_Cube)
			pRawTexture = new IDirect3DTexture9();
		else
			pRawTexture = new IDirect3DCubeTexture9();
		m_pDevTexture = new CDeviceTexture(pRawTexture);
#if defined(ENABLE_X360_TEXTURE_CAPTURE)
		HRESULT hr = PIXReportNewTexture(m_pDevTexture->GetBaseTexture());
		assert(SUCCEEDED(hr));
#endif
	}
	D3DFormat d3dFmt = DeviceFormatFromTexFormat(GetDstFormat());
	if( m_bIsSRGB && gRenDev->IsLinearSpaceShadingEnabled() )
		d3dFmt = ConvertToSRGBFmt(d3dFmt);
	if(m_eTT != eTT_Cube)
	{
		DWORD dwBaseAddress = m_pFileTexMips->m_pPoolItem->m_pDevTexture->Get2DTexture()->Format.BaseAddress << GPU_TEXTURE_ADDRESS_SHIFT;
		XGSetTextureHeader(m_pFileTexMips->m_pPoolItem->m_pOwner->m_Width, m_pFileTexMips->m_pPoolItem->m_pOwner->m_Height, m_pFileTexMips->m_pPoolItem->m_pOwner->m_nMips, STREAMED_TEXTURE_USAGE & ~USAGE_CUSTOM, d3dFmt,
			0,		// Pool - ignored
			dwBaseAddress,		// BaseOffset
			XGHEADER_CONTIGUOUS_MIP_OFFSET,		// MipOffset
			0,		// Pitch(default)
			m_pDevTexture->Get2DTexture(), NULL, NULL);
	}
	else
	{
		DWORD dwBaseAddress = m_pFileTexMips->m_pPoolItem->m_pDevTexture->GetCubeTexture()->Format.BaseAddress << GPU_TEXTURE_ADDRESS_SHIFT;
		XGSetCubeTextureHeader(m_pFileTexMips->m_pPoolItem->m_pOwner->m_Width, m_pFileTexMips->m_pPoolItem->m_pOwner->m_nMips, STREAMED_TEXTURE_USAGE & ~USAGE_CUSTOM, d3dFmt,
			0,		// Pool - ignored
			dwBaseAddress,		// BaseOffset
			XGHEADER_CONTIGUOUS_MIP_OFFSET,		// MipOffset
			m_pDevTexture->GetCubeTexture(), NULL, NULL);
	}
#endif

	m_nMinMipVidUploaded = nMinMip;


























}

STexPoolItem *CTexture::StreamGetPoolItem(int nStartMip, int nMips, bool bShouldBeCreated)
{
	if (!m_pFileTexMips)
		return NULL;

	assert (nStartMip < m_nMips);
	assert(!m_bStreamingInProgress);

	SCOPED_RENDERER_ALLOCATION_NAME_HINT( GetSourceName() );

	const STexStreamingInfo::SMipHeader *mh = m_pFileTexMips->m_pMipHeader;

	if (m_pFileTexMips->m_pPoolItem && m_pFileTexMips->m_pPoolItem->m_pOwner)
		if (m_pFileTexMips->m_pPoolItem->m_pOwner->m_nMips == nMips && 
				m_pFileTexMips->m_pPoolItem->m_pOwner->m_Width == mh[nStartMip].m_USize && 
				m_pFileTexMips->m_pPoolItem->m_pOwner->m_Height == mh[nStartMip].m_VSize)
			return NULL;

	D3DFormat d3dFmt = DeviceFormatFromTexFormat(GetDstFormat());
	if( m_bIsSRGB && gRenDev->IsLinearSpaceShadingEnabled() )
		d3dFmt = ConvertToSRGBFmt(d3dFmt);

	STexPool* pPool = CTexture::StreamCreatePool(mh[nStartMip].m_USize, mh[nStartMip].m_VSize, nMips, d3dFmt, m_eTT);
	if (!pPool)
		return NULL;

	AUTO_LOCK(STexPoolItem::s_sSyncLock);

	// Try to find empty item in the pool
	STexPoolItem *pIT = pPool->m_ItemsList.m_Next;
	while (pIT != &pPool->m_ItemsList)
	{
		if (pIT->m_NextFree && !pIT->IsStillUsedByGPU())
			break;
		pIT = pIT->m_Next;
	}
	if (pIT != &pPool->m_ItemsList && ((pIT->m_pDevTexture && pIT->m_pDevTexture->GetBaseTexture()) || pIT->m_nDeviceTexSize))
	{
		pIT->UnlinkFree();
#ifdef DO_RENDERLOG
		if (CRenderer::CV_r_logTexStreaming == 2)
			gRenDev->LogStrv(SRendItem::m_RecurseLevel[gRenDev->m_RP.m_nProcessThreadID], "Remove from FreePool '%s', [%d x %d], Size: %d\n", m_SrcName.c_str(), pIT->m_pOwner->m_Width, pIT->m_pOwner->m_Height, pIT->m_pOwner->m_Size);
#endif
	}
	else
	{
		pIT = new STexPoolItem;

		// Create API texture for the item in DEFAULT pool
		HRESULT h = S_OK;
		if(m_eTT!=eTT_Cube)
			h=gcpRendD3D->m_DevMan.Create2DTexture(mh[nStartMip].m_USize, mh[nStartMip].m_VSize, nMips, STREAMED_TEXTURE_USAGE, d3dFmt, D3DPOOL_DEFAULT, &pIT->m_pDevTexture, 0, bShouldBeCreated);
		else
			h=gcpRendD3D->m_DevMan.CreateCubeTexture(mh[nStartMip].m_USize, nMips, STREAMED_TEXTURE_USAGE, d3dFmt, D3DPOOL_DEFAULT, &pIT->m_pDevTexture, 0, bShouldBeCreated);
		if (FAILED(h) || !pIT->m_pDevTexture || !pIT->m_pDevTexture->GetBaseTexture())
    {
      delete pIT;
      return NULL;
    }
		pIT->m_pOwner = pPool;
		pIT->Link(&pPool->m_ItemsList);
		CryInterlockedAdd(CTexture::s_nStatsCurManagedStreamedTexMem.Addr(), pPool->m_Size);

#if !defined _RELEASE
		gEnv->pRenderer->AddTexturePoolAllocation(pPool->m_Size,m_SrcName);
#	if CAPTURE_REPLAY_LOG
		CryGetIMemReplay()->AddTexturePoolContext(pIT->m_pDevTexture->GetBaseAddress(), nStartMip, pIT->m_pOwner->m_Width,  pIT->m_pOwner->m_Height,m_SrcName.c_str(),m_nFlags);
#	endif //#if CAPTURE_REPLAY_LOG
#	if defined(ENABLE_X360_TEXTURE_CAPTURE)
		h = PIXSetTextureName(pIT->m_pDevTexture->GetBaseTexture(), m_SrcName.c_str());
		assert(SUCCEEDED(h));
#	endif
#endif
	}
	return pIT;
}

STexPool *CTexture::StreamCreatePool(int nWidth, int nHeight, int nMips, D3DFormat eTF, ETEX_Type eTT)
{
	uint32 i;
	STexPool *pPool = NULL;

	for (i=0; i<s_TexturesPools.Num(); i++)
	{
		pPool = s_TexturesPools[i];
		if (pPool->m_nMips == nMips && pPool->m_Width == nWidth && pPool->m_Height == nHeight && pPool->m_eFormat == eTF && pPool->m_eTT == eTT)
			break;
	}
	// Create new pool
	if (i == s_TexturesPools.Num())
	{
		pPool = new STexPool;
		pPool->m_eTT = eTT;
		pPool->m_eFormat = eTF;
		pPool->m_Width = nWidth;
		pPool->m_Height = nHeight;
		pPool->m_nMips = nMips;
		pPool->m_Size = CTexture::TextureDataSize(nWidth, nHeight, 1, nMips, TexFormatFromDeviceFormat(eTF));
		if (eTT == eTT_Cube)
			pPool->m_Size *= 6;

#if defined (DIRECT3D9) && (!defined(XENON) && !defined(PS3) && !defined(CAFE))
		CDeviceTexture *pDevTexture = NULL;
		HRESULT hr = S_OK;
		if (eTT != eTT_Cube)
			hr = gcpRendD3D->m_DevMan.Create2DTexture(nWidth, nHeight, nMips, STREAMED_TEXTURE_USAGE, eTF, D3DPOOL_SYSTEMMEM, &pDevTexture, NULL);
		else
			hr = gcpRendD3D->m_DevMan.CreateCubeTexture(nWidth, nMips, STREAMED_TEXTURE_USAGE, eTF, D3DPOOL_SYSTEMMEM, &pDevTexture, NULL);
		if(FAILED(hr))
			return NULL;
		pPool->m_pSysTexture = pDevTexture;
#endif

		pPool->m_ItemsList.m_Next = &pPool->m_ItemsList;
		pPool->m_ItemsList.m_Prev = &pPool->m_ItemsList;
		CTexture::s_TexturesPools.AddElem(pPool);
	}

	return pPool;
}

void CTexture::StreamUpdatePool()
{
	CHK_RENDTH;

	FUNCTION_PROFILER_FAST(GetISystem(), PROFILE_RENDERER, g_bProfilerEnabled);

  if (!CRenderer::CV_r_texturesstreaming)
    return;

#ifndef _RELEASE
	// Debug/profile checks and info
	if(CRenderer::CV_r_TexturesStreamingDebug >= 3)
		OutputDebugInfo();
	if (CRenderer::CV_r_texturesstreampoolsize < 10)
		CRenderer::CV_r_texturesstreampoolsize = 10;
	CTexture::StreamValidateTexSize();
#endif














	// Get the current pool size
	int nCurTexPoolSize = CTexture::s_nStatsCurManagedStreamedTexMem;
  int nCurTexPoolSysSize = CTexture::s_nStatsCurManagedStreamedSysTexMem;

	// set up the limits
  const int nMaxPoolSize = CRenderer::GetTexturesstreampoolsize()*1024*1024;
	const int nLowerPoolLimit = (int)(nMaxPoolSize*0.8f);
  const int nMaxPoolSysSize = CRenderer::CV_r_texturesstreampoolsyssize*1024*1024;
  const int nLowerPoolSysLimit = (int)(nMaxPoolSysSize*0.95f);

	// indicate pool overflow
	CTexture::s_bPoolOverflow = nCurTexPoolSize >= (int)(nMaxPoolSize*0.9f);

  // Phase1: delete old freed pool items
	StreamPoolGarbageCoolection(&nCurTexPoolSize, nLowerPoolLimit);

	// don't need further processing
	if (nCurTexPoolSize < nLowerPoolLimit 
#if !defined(PS3) && !defined(XENON)
      && nCurTexPoolSysSize < nLowerPoolSysLimit
#endif
      && !CRenderer::CV_r_TexturesStreamingDebug)
	{
		s_nStreamTimeSlicingTexID = 0;
		return;
	}

	// Phase2: unload old/unused textures
	StreamTraverseUpdateTextures(&nCurTexPoolSize, nLowerPoolLimit, &nCurTexPoolSysSize, nLowerPoolSysLimit);

  // Usually we do it in case of textures thrashing
	const int nHigherPoolLimit = (int)(nMaxPoolSize*0.97f);
  if (nCurTexPoolSize >= nHigherPoolLimit)
    StreamHandlePoolOverflow(&nCurTexPoolSize, nLowerPoolLimit, nHigherPoolLimit);
}

void CTexture::StreamPoolGarbageCoolection(int* nCurTexPoolSize, int nLowerPoolLimit)
{
	AUTO_LOCK(STexPoolItem::s_sSyncLock);
	int nMaxItemsToFree = s_bPoolOverflow ? 1000 : 10;
	STexPoolItem *pIT = CTexture::s_FreeTexPoolItems.m_PrevFree;
	while (pIT != &CTexture::s_FreeTexPoolItems)
	{
		assert (!pIT->m_pTex);
		STexPoolItem *pITNext = pIT->m_PrevFree;
		if (!pIT->IsStillUsedByGPU())
		{
			const int nOwnersSize = pIT->m_pOwner ? pIT->m_pOwner->m_Size : 0;
      *nCurTexPoolSize -= pIT->m_pDevTexture ? max((int)pIT->m_pDevTexture->GetDeviceSize(), nOwnersSize) : max((int)pIT->m_nDeviceTexSize, nOwnersSize); 
			delete pIT;
			nMaxItemsToFree--;
		}
		pIT = pITNext;
		// we release all textures immediately on consoles
#if !defined(XENON) && !defined(PS3)
		if (nMaxItemsToFree <= 0 || *nCurTexPoolSize < nLowerPoolLimit)
			break;
#endif
	}
	CTexture::StreamValidateTexSize();
}

void CTexture::StreamTraverseUpdateTextures(int* nCurTexPoolSize, int nLowerPoolLimit, int* nCurSysTexPoolSize, int nLowerSysPoolLimit)
{
	// if we processed the whole list to the end then do a new sort 
	if(s_nStreamTimeSlicingTexID == 0)
	{
		const int nCurFrameUpdateId = gRenDev->m_RP.m_TI[gRenDev->m_RP.m_nProcessThreadID].m_nFrameUpdateID;
		CompareStreamingPrioriryInv sort_op(gRenDev->m_RP.m_TI[gRenDev->m_RP.m_nProcessThreadID].m_arrZonesRoundId, nCurFrameUpdateId);
		std::sort(&s_DistanceSortedTextures[0], &s_DistanceSortedTextures[0] + s_DistanceSortedTextures.size(), sort_op);
	}

	// unload all textures aggressively in debug mode
	if (CRenderer::CV_r_TexturesStreamingDebug)
		nLowerPoolLimit = 0;

	int nItemsToTraverse = s_bPoolOverflow ? 2500 : 500;

	const int nCurrentFarZoneRoundId = gRenDev->m_RP.m_TI[gRenDev->m_RP.m_nProcessThreadID].m_arrZonesRoundId[MAX_PREDICTION_ZONES-1];
	const int nCurrentNearZoneRoundId = gRenDev->m_RP.m_TI[gRenDev->m_RP.m_nProcessThreadID].m_arrZonesRoundId[0];

	// the main processing list
	int i = s_nStreamTimeSlicingTexID;
	for (;i<(int)s_DistanceSortedTextures.size();++i)
	{
		PrefetchLine(&s_DistanceSortedTextures[0] + i + 1, 0);
		CTexture* tp = s_DistanceSortedTextures[i];

		if(tp->IsUnloaded() || tp->m_bStreamingInProgress || !tp->m_bStreamPrepared || tp->m_bStreamRequested)
			continue;

		// update current mip if object left near area
		if (nCurrentNearZoneRoundId - tp->m_pFileTexMips->m_arrSPInfo[0].nRoundUpdateId > 3)
			if (nCurrentFarZoneRoundId - tp->m_pFileTexMips->m_arrSPInfo[MAX_PREDICTION_ZONES-1].nRoundUpdateId < 3)
			{
				tp->CheckEnqueueForStreaming(tp->m_pFileTexMips->m_arrSPInfo[MAX_PREDICTION_ZONES-1].fLastMinMipFactor,	tp->m_pFileTexMips->m_arrSPInfo[MAX_PREDICTION_ZONES-1].bLastHighPriority);
			}

		// unload textures that are older than 4 update cycles
		if ((nCurrentFarZoneRoundId - tp->m_pFileTexMips->m_arrSPInfo[MAX_PREDICTION_ZONES-1].nRoundUpdateId > 3) && 
				(nCurrentNearZoneRoundId - tp->m_pFileTexMips->m_arrSPInfo[0].nRoundUpdateId > 3))
		{




      int nFullSize = tp->GetDevTexture() ? tp->GetDataSize() : 0;
			*nCurTexPoolSize -= tp->StreamUnload(true);
      *nCurSysTexPoolSize -= nFullSize;
			--i;	// texture tp has just got deleted itself from the list, so we need to adjust the iterator

		}
		// check if we don't need some mips in this texture anymore
		else if(tp->m_pFileTexMips->m_nMinMipCur > tp->m_nMinMipVidUploaded && !(tp->m_pFileTexMips->m_eStreamingInfoFlags & STexStreamingInfo::eStreamingInfo_Resident))
		{
			*nCurTexPoolSize -= tp->StreamSetLod(tp->m_pFileTexMips->m_nMinMipCur);
		}

    // lazy traversal check
#if !defined (PS3) && !defined(XENON)
    if ((*nCurSysTexPoolSize < nLowerSysPoolLimit && *nCurTexPoolSize < nLowerPoolLimit) || --nItemsToTraverse == 0)
      break;



#endif
	}

	// store the current texture id for the future traversal
	s_nStreamTimeSlicingTexID = max((int)0, i) % max((size_t)1, s_DistanceSortedTextures.size());

	CTexture::StreamValidateTexSize();
}

void CTexture::StreamHandlePoolOverflow(int* nCurTexPoolSize, int nLowerPoolLimit, int nHigherPoolLimit)
{
	const int nCurFrameId = gRenDev->m_RP.m_TI[gRenDev->m_RP.m_nProcessThreadID].m_nFrameUpdateID;

	// output warning
	if(*nCurTexPoolSize >= nHigherPoolLimit && nCurFrameId % 60 == 0)	// once per 2 seconds
		iLog->Log("WARNING: Texture pool overflow, insufficient: %dKB", CRenderer::GetTexturesstreampoolsize()*1024 - s_nStatsCurManagedStreamedTexMem / 1024);

	// release all possible mips
	size_t nUnloadedItems = 0;
	CTexture** pBegin = NULL;
	const size_t kNumDistanceSortedTextures = s_DistanceSortedTextures.size();
	if(kNumDistanceSortedTextures)
		pBegin = &s_DistanceSortedTextures[0];
	for(size_t i = 0;i<kNumDistanceSortedTextures;++i)
	{
		PrefetchLine(pBegin, i + 1);

		CTexture* tp = s_DistanceSortedTextures[i];
		if (tp->m_bStreamingInProgress || tp->m_pFileTexMips->m_eStreamingInfoFlags & STexStreamingInfo::eStreamingInfo_Resident)
			continue;

		if (nCurFrameId - tp->m_nAccessFrameID <= 1)
			break;

		// unload textures that are older than 4 update cycles
		const int nMaxPossibleMip = tp->m_nMips - tp->m_CacheFileHeader.m_nMipsPersistent;
		if(tp->m_nMinMipVidUploaded < nMaxPossibleMip)
		{
			*nCurTexPoolSize -= tp->StreamSetLod(tp->m_nMinMipVidUploaded + 1);
			if (CRenderer::CV_r_TexturesStreamingDebug == 2)
				iLog->Log("Aggressively unloading texture: %s - From mip: %i, To mip: %i", tp->m_SrcName.c_str(), tp->m_nMinMipVidUploaded, tp->m_nMinMipVidUploaded + 1);
			++nUnloadedItems;
		}

		if(*nCurTexPoolSize < nLowerPoolLimit)
			break;
	}

	s_bPoolOverflow = *nCurTexPoolSize >= nLowerPoolLimit;

#ifndef _RELEASE
	// output overflow warning
	if (nUnloadedItems == 0)
	{
#	if !defined(XENON) && !defined(PS3) && !defined(CAFE)
		if ( !gEnv->pSystem->IsDevMode() )
		{
			_SetVar("r_TexturesStreamPoolSize", s_nStatsCurManagedStreamedTexMem/(1024*1024)+30);
			if(nCurFrameId % 60 == 0)
				iLog->Log("WARNING: Texture pool was changed to %d Mb", CRenderer::GetTexturesstreampoolsize());
		}



#	endif
	}
#endif
}

void CTexture::StreamCopyMipsTexToTex( STexPoolItem* pSrcItem, int nMipSrc, STexPoolItem* pDestItem, int nMipDest, int nNumMips )
{
	CHK_RENDTH;

	HRESULT hr = S_OK;
	assert(pSrcItem && pDestItem);
	assert(pSrcItem->m_pOwner->m_eTT == pDestItem->m_pOwner->m_eTT);
	const int32 nSides = pDestItem->m_pOwner->m_eTT != eTT_Cube ? 1 : 6;
	for(int32 iSide = 0;iSide < nSides;++iSide)
	{
		const int32 iSideLockIndex = pDestItem->m_pOwner->m_eTT != eTT_Cube ? -1 : iSide;

		assert(nMipSrc >= 0 && nMipDest >= 0);
	#if defined (DIRECT3D9) && !defined(XENON)
		assert(pSrcItem->m_pDevTexture->GetBaseTexture()->GetLevelCount() >= nMipSrc + nNumMips);
		assert(pDestItem->m_pDevTexture->GetBaseTexture()->GetLevelCount() >= nMipDest + nNumMips);
		D3DTexture* pSrc = pSrcItem->m_pDevTexture->Get2DTexture();
		D3DCubeTexture* pSrcCube = pSrcItem->m_pDevTexture->GetCubeTexture();
		D3DTexture* pDest = pDestItem->m_pDevTexture->Get2DTexture();
		D3DCubeTexture* pDestCube = pDestItem->m_pDevTexture->GetCubeTexture();
		assert(pSrc && pDest);
		for(int i=0;i<nNumMips;++i)
		{
			D3DSurface* pSurfSrc = NULL;
			D3DSurface* pSurfDest = NULL;
			if(pDestItem->m_pOwner->m_eTT != eTT_Cube)
			{
				pSrc->GetSurfaceLevel(nMipSrc + i, &pSurfSrc);
				pDest->GetSurfaceLevel(nMipDest + i, &pSurfDest);
			}
			else
			{
				pSrcCube->GetCubeMapSurface((D3DCUBEMAP_FACES)iSide, nMipSrc + i, &pSurfSrc);
				pDestCube->GetCubeMapSurface((D3DCUBEMAP_FACES)iSide, nMipDest + i, &pSurfDest);
			}
			hr = gcpRendD3D->m_pd3dDevice->StretchRect(pSurfSrc,NULL,pSurfDest,NULL,D3DTEXF_NONE);
			assert(SUCCEEDED(hr));

			SAFE_RELEASE(pSurfSrc);
			SAFE_RELEASE(pSurfDest);
		}
		assert(SUCCEEDED(hr));





































































	#elif defined(DIRECT3D10)
		for(int i=0;i<nNumMips;++i)
			gcpRendD3D->m_pd3dDeviceContext->CopySubresourceRegion(pDestItem->m_pDevTexture->GetBaseTexture(), 
																		D3D11CalcSubresource(nMipDest + i, iSide, pDestItem->m_pOwner->m_nMips),
																		0, 0, 0, pSrcItem->m_pDevTexture->GetBaseTexture(), 
																		D3D11CalcSubresource(nMipSrc + i, iSide, pSrcItem->m_pOwner->m_nMips), 
																		NULL);

	#endif
	}
}

// Debug routines /////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef _RELEASE
struct StreamDebugSortFunc
{
	bool operator()(const CTexture* p1, const CTexture* p2) const
	{
		if(!p1)
			return true;
		if(!p2)
			return false;
		if(p1->GetActualSize() != p2->GetActualSize())
			return p1->GetActualSize() < p2->GetActualSize();
		return p1 < p2;
	}
};

void CTexture::OutputDebugInfo()
{
	CD3D9Renderer *r = gcpRendD3D;
	D3DDevice *dv = r->GetD3DDevice();

	int nX = 40;
	int nY = 30;

	if(CRenderer::CV_r_TexturesStreamingDebugDumpIntoLog)
	{
		Vec3i vCamPos = iSystem->GetViewCamera().GetPosition();
		CryLogAlways("===================== Dumping textures streaming debug info for camera position (%d, %d, %d) =====================", vCamPos.x, vCamPos.y, vCamPos.z);
	}

	char szText[512];
	sprintf(szText, "Size |  MF   | P |  Mips  |  N  | F | Name");
	r->WriteXY(nX,nY,1.f,1.f,1,1,0,1,szText);
	if(CRenderer::CV_r_TexturesStreamingDebugDumpIntoLog)
		CryLogAlways(szText);

	std::vector<CTexture*> texSorted(s_DistanceSortedTextures);

	const char* const sTexFilter = CRenderer::CV_r_TexturesStreamingDebugfilter->GetString();
	const bool bNameFilter = sTexFilter!=0 && strlen(sTexFilter) > 1;

	if(CRenderer::CV_r_TexturesStreamingDebug == 4 && !texSorted.empty())
	{



		std::stable_sort(&texSorted[0], &texSorted[0] + texSorted.size(), StreamDebugSortFunc());

	}

	for(int i = (int)texSorted.size() - 1, nTexNum = 0;i >= 0;--i)
	{
		CTexture* tp = texSorted[i];
		if(tp == NULL)
			continue;
		// name filter
		if(bNameFilter!=0 && !strstr(tp->m_SrcName.c_str(), sTexFilter))
			continue;
		if(tp->m_nActualSize/1024 < CRenderer::CV_r_TexturesStreamingDebugMinSize)
			continue;

		ColorF color = (tp->m_nActualSize/1024 >= CRenderer::CV_r_TexturesStreamingDebugMinSize*2) ? Col_Red : Col_Green;

		// compute final mip factor
		bool bHighPriority = false;
		float fFinalMipFactor = pow(99.99f,2.f);
		for(int z=0; z<MAX_PREDICTION_ZONES; z++)
			if(tp->m_pFileTexMips && tp->m_pFileTexMips->m_arrSPInfo[z].nRoundUpdateId > gRenDev->m_RP.m_TI[gRenDev->m_RP.m_nProcessThreadID].m_arrZonesRoundId[z]-2)
			{
				fFinalMipFactor = min(fFinalMipFactor, tp->m_pFileTexMips->m_arrSPInfo[z].fLastMinMipFactor);
				bHighPriority |= tp->m_pFileTexMips->m_arrSPInfo[z].bLastHighPriority;
			}

		// how many times used in area around
		assert(tp->m_pFileTexMips);
		int nCounter = tp->m_pFileTexMips->m_nLastPrioriryUpdateCounter;
		
		int nMipIdSigned = tp->StreamCalculateMipsSigned(fFinalMipFactor);

		if(nMipIdSigned > CRenderer::CV_r_TexturesStreamingDebugMinMip)
			continue;

		const bool bResident = tp->m_pFileTexMips->m_eStreamingInfoFlags & STexStreamingInfo::eStreamingInfo_Resident;

		sprintf(szText, "%.2f |%6.2f | %1d | %2d/%d/%d | %3d | %s | %s", 
			(float)tp->m_nActualSize/1024/1024, 
			sqrtf(fFinalMipFactor),
			(int)bHighPriority,
			tp->GetNumMipsNonVirtual()-nMipIdSigned, tp->GetNumMipsNonVirtual() - tp->m_nMinMipVidUploaded, tp->GetNumMipsNonVirtual(), 
			nCounter,
			bResident ? "R" : " ",
			tp->m_SrcName.c_str());

		r->WriteXY(nX,nY+(nTexNum+1)*10,1.f,1.f,color.r,color.g,color.b,1, szText);
		if(CRenderer::CV_r_TexturesStreamingDebugDumpIntoLog)
			CryLogAlways(szText);

		++nTexNum;
		if(nTexNum > 50 && !CRenderer::CV_r_TexturesStreamingDebugDumpIntoLog)
			break;
	}

	if(CRenderer::CV_r_TexturesStreamingDebugDumpIntoLog)
		CryLogAlways("==============================================================================================================");

	if(ICVar * pCVar = gEnv->pConsole->GetCVar("r_TexturesStreamingDebugDumpIntoLog"))
		pCVar->Set(0);
}
#endif
